# -*- coding: utf-8 -*-

from decimal import Decimal

from common.utils import math_helper
from common.lottery.cyclical.ff_ks.model.order import BET_TYPE

RATE_CONFIG = {  # base on UNIT==2
    BET_TYPE.THREE_SUM: [
        216.000, 72.000, 36.000, 21.600, 14.400, 10.286, 8.640, 8.000,
        8.000, 8.640, 10.286, 14.400, 21.600, 36.000, 72.000, 216.000],  # 3-18的奖金
    BET_TYPE.THREE_SAME_SINGLE: 216.000,
    BET_TYPE.THREE_SAME_ALL: 36.000,
    BET_TYPE.TWO_SAME_SINGLE: 72.000,
    BET_TYPE.TWO_SAME_MULTI: 13.500,
    BET_TYPE.THREE_DIFF: 36.000,
    BET_TYPE.TWO_DIFF: 7.200,
    BET_TYPE.THREE_SEQ_ALL: 9.000,
    BET_TYPE.ONE_ANY: 2.374,
    BET_TYPE.PLATE_SUM_BSOE: 2.000,
}

UNIT = 2  # 彩票单价


def valid_number(bet_type, number):
    '''比较简单，有些玩法没有number，选项大部分用逗号隔开
    '''
    if bet_type in (BET_TYPE.THREE_SAME_ALL, BET_TYPE.THREE_SEQ_ALL):
        assert number == '', 'number should empty string'
        return
    positions = number.split('|')
    nums = []
    for position in positions:
        ds = position.split(',')
        for d in ds:
            if bet_type == BET_TYPE.THREE_SUM:
                assert 3 <= int(d) <= 18, 'number range error'
            elif bet_type == BET_TYPE.PLATE_SUM_BSOE:
                assert d in ['b', 's', 'o', 'e'], 'PLATE_SUM_BSOE number error'
                return
            else:
                assert 1 <= int(d) <= 6, 'number range error'
        nums.append(ds)

    if bet_type == BET_TYPE.THREE_SUM:
        # 用逗号分割的和值的候选值
        assert len(positions) == 1 and len(nums) == 1, 'no `|` expected'
        assert 1 <= len(nums[0]) <= 16, 'three sum format error'
    elif bet_type == BET_TYPE.THREE_SAME_SINGLE:
        # 用逗号分割的重复数的候选值
        assert len(positions) == 1 and len(nums) == 1, 'no `|` expected'
        assert 1 <= len(nums[0]) <= 6, 'three same single number format error'
    elif bet_type == BET_TYPE.TWO_SAME_SINGLE:
        # ‘|’前是对子，后面是单子，二者不能有交集
        assert len(positions) == 2, 'two same number format error'
        assert len(set(nums[0]) & set(nums[1])) == 0, 'two same format error'
    elif bet_type == BET_TYPE.TWO_SAME_MULTI:
        # 用逗号分割的重复数的候选值
        assert len(positions) == 1 and len(nums) == 1, 'no `|` expected'
        assert 1 <= len(nums[0]) <= 6, 'two same number format error'
    elif bet_type == BET_TYPE.THREE_DIFF:
        # 用逗号分割的候选值
        assert len(positions) == 1 and len(nums) == 1, 'no `|` expected'
        assert 3 <= len(set(nums[0])) <= 6, 'three diff number format error'
    elif bet_type == BET_TYPE.TWO_DIFF:
        # 用逗号分割的候选值
        assert len(positions) == 1 and len(nums) == 1, 'no `|` expected'
        assert 2 <= len(set(nums[0])) <= 6, 'two diff number format error'
    elif bet_type == BET_TYPE.ONE_ANY:
        assert len(positions) == 1 and len(nums) == 1, 'no `|` expected'
        assert 1 <= len(set(nums[0])) <= 6, 'number format error'
    else:
        assert False, 'bet_type invalid'


def calc_total_price(bet_type, number, times=1, unit=UNIT):
    '''根据不同的竞猜方式计算所需总价
    '''
    nums = number.split(',')
    if bet_type in (BET_TYPE.THREE_SUM, BET_TYPE.THREE_SAME_SINGLE,
                    BET_TYPE.TWO_SAME_MULTI, BET_TYPE.ONE_ANY, BET_TYPE.PLATE_SUM_BSOE):
        return len(nums) * times * unit
    elif bet_type == BET_TYPE.TWO_SAME_SINGLE:
        positions = number.split('|')
        targets = []
        for position in positions:
            targets.append(position.split(','))
        n = len(targets[0]) * len(targets[1])
        return n * times * unit
    elif bet_type == BET_TYPE.THREE_DIFF:
        # 任意3个的组合，c(n, 3)
        return math_helper.c(len(nums), 3) * times * unit
    elif bet_type == BET_TYPE.TWO_DIFF:
        # 任意2个的组合，c(n, 2)
        return math_helper.c(len(nums), 2) * times * unit
    elif bet_type in (BET_TYPE.THREE_SAME_ALL, BET_TYPE.THREE_SEQ_ALL):
        return times * unit
    else:
        assert False, 'bet_type %s invalid' % bet_type


def generate_win_func(number):
    '''根据开奖结果生成根据押注方式对应的中奖函数，避免重复计算
    '''
    result = {}

    def three_sum(x):
        guesses = x.split(',')
        real_sum = str(sum([int(n) for n in number]))
        return int(real_sum) - 3 if real_sum in guesses else -1

    result[BET_TYPE.THREE_SUM] = three_sum

    def three_same_single(x):
        if len(set(number)) != 1:
            return 0
        guesses = x.split(',')
        return 1 if number[0] in guesses else 0

    result[BET_TYPE.THREE_SAME_SINGLE] = three_same_single

    def two_same_single(x):
        uniques = set(number)
        if len(uniques) != 2:
            return 0
        dup = 0
        for n in number:
            dup ^= int(n)
        single = dup
        for n in uniques:
            dup ^= int(n)
        dup_guesses, single_guesses = x.split('|')
        if str(dup) in dup_guesses.split(',') and \
                str(single) in single_guesses.split(','):
            return 1
        return 0

    result[BET_TYPE.TWO_SAME_SINGLE] = two_same_single

    def two_same_multi(x):
        guesses = x.split(',')
        uniques = set(number)
        if len(uniques) > 2:
            return 0
        elif len(uniques) == 1:
            return 1 if number[0] in guesses else 0
        else:
            dup = 0
            for n in number:
                dup ^= int(n)
            for n in uniques:
                dup ^= int(n)
            return 1 if str(dup) in guesses else 0

    result[BET_TYPE.TWO_SAME_MULTI] = two_same_multi

    def three_diff(x):
        if len(set(x.split(',')) & set(number)) == 3:
            return 1
        return 0

    result[BET_TYPE.THREE_DIFF] = three_diff

    def two_diff(x):
        hits = len(set(x.split(',')) & set(number))
        if hits < 2:
            return 0
        return math_helper.c(hits, 2)

    result[BET_TYPE.TWO_DIFF] = two_diff

    def three_same_all(x):
        return 1 if len(set(number)) == 1 else 0

    result[BET_TYPE.THREE_SAME_ALL] = three_same_all

    def three_seq_all(x):
        if number in ('123', '234', '345', '456'):
            return 1
        return 0

    result[BET_TYPE.THREE_SEQ_ALL] = three_seq_all

    def one_any(x):
        return len(set(number) & set(x.split(',')))

    result[BET_TYPE.ONE_ANY] = one_any

    def sum_plate_bsoe(x):
        num = sum([int(n) for n in number])
        desc = set()
        if num >= 11:
            desc.add('b')
        else:
            desc.add('s')
        if num % 2 == 0:
            desc.add('e')
        else:
            desc.add('o')
        guesses = x.split('|')
        hit = 1
        for i, guess in enumerate(guesses):
            t = guess.split(',')
            hit *= len(set(t) & desc)
        return hit

    result[BET_TYPE.PLATE_SUM_BSOE] = sum_plate_bsoe

    return result


def calc_win_price(funcs, bet_number, bet_type, times=1, unit=UNIT):
    '''根据开奖结果和押注方式/倍数，计算中奖金额
    '''
    func = funcs.get(bet_type)
    base = 0
    if bet_type == BET_TYPE.THREE_SUM:
        index = func(bet_number)
        if index >= 0:
            base = RATE_CONFIG.get(bet_type)[index]
    else:
        base = func(bet_number) * Decimal(RATE_CONFIG.get(bet_type))

    return Decimal(base) * Decimal(times) * Decimal(unit) / Decimal(UNIT)
